package query.select

import ast.statement.select.SqlUnionType
import database.DBConnection
import dsl.QuerySub
import dsl.QueryTableColumn
import util.humpToLine
import java.math.BigDecimal
import java.sql.Connection
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.declaredMembers
import kotlin.reflect.jvm.javaField

/**
 * 查询dsl基类
 */
abstract class SelectQueryImpl : SelectQuery {
    /**
     * 生成union查询
     * 例如：(Select() from Table1) union (Select() from Table2)
     * @param select SelectQuery union右侧的查询dsl
     * @return UnionSelect union查询dsl
     */
    override infix fun union(select: SelectQuery): UnionSelect {
        return UnionSelect(this, SqlUnionType.UNION, select, this.getDbType())
    }

    /**
     * 生成union all查询
     * 例如：(Select() from Table1) unionAll (Select() from Table2)
     * @param select SelectQuery union右侧的查询dsl
     * @return UnionSelect union查询dsl
     */
    override infix fun unionAll(select: SelectQuery): UnionSelect {
        return UnionSelect(this, SqlUnionType.UNION_ALL, select, this.getDbType())
    }

    /**
     * 生成except查询
     * 例如：(Select() from Table1) except (Select() from Table2)
     * @param select SelectQuery union右侧的查询dsl
     * @return UnionSelect union查询dsl
     */
    override infix fun except(select: SelectQuery): UnionSelect {
        return UnionSelect(this, SqlUnionType.EXCEPT, select, this.getDbType())
    }

    /**
     * 生成intersect查询
     * 例如：(Select() from Table1) interSect (Select() from Table2)
     * @param select SelectQuery union右侧的查询dsl
     * @return UnionSelect union查询dsl
     */
    override infix fun interSect(select: SelectQuery): UnionSelect {
        return UnionSelect(this, SqlUnionType.INTERSECT, select, this.getDbType())
    }

    /**
     * 调用jdbc查询sql并返回结果
     * @param sql String 查询sql语句
     * @param isTransaction Boolean 是否是事务
     * @param conn Connection 数据库连接
     * @param dbConnection DBConnection? 基础查询类
     * @return List<Map<String, Any?>> 返回查询结果
     */
    fun query(
        sql: String,
        isTransaction: Boolean,
        conn: Connection,
        dbConnection: DBConnection?
    ): List<Map<String, Any?>> {
        return if (isTransaction) {
            database.query(conn, sql)
        } else {
            val connection = if (conn.isClosed) {
                dbConnection?.getConnection()
            } else {
                conn
            }
            val tempResult = database.query(connection!!, sql)
            connection.close()
            tempResult
        }
    }

    /**
     * 调用jdbc查询sql并返回条数
     * @param sql String 查询sql语句
     * @param isTransaction Boolean 是否是事务
     * @param conn Connection 数据库连接
     * @param dbConnection DBConnection? 基础查询类
     * @return Long 返回条数
     */
    fun queryCount(
        sql: String,
        isTransaction: Boolean,
        conn: Connection,
        dbConnection: DBConnection?
    ): Long {
        val result = if (isTransaction) {
            database.queryCount(conn, sql)
        } else {
            val connection = if (conn.isClosed) {
                dbConnection?.getConnection()
            } else {
                conn
            }
            val tempResult = database.queryCount(connection!!, sql)
            connection.close()
            tempResult
        }

        return result.toLong()
    }

    /**
     * 将数据绑定到实体类中
     * @param clazz Class<T> 实体类的java class
     * @param result List<Map<String, Any?>> 数据库查询结果集
     * @return List<T> 绑定结果集
     */
    @Suppress("UNCHECKED_CAST")
    fun <T : Any> bind(clazz: Class<T>, result: List<Map<String, Any?>>): List<T> {
        when (clazz) {
            Int::class.java, Long::class.java, Float::class.java, Double::class.java, String::class.java, Date::class.java, BigDecimal::class.java -> {
                return result.map {
                    val keys = it.keys.toList()
                    val key = keys[0]
                    it[key] as T
                }
            }

            Pair::class.java -> {
                return result.map {
                    val keys = it.keys.toList()
                    (it[keys[0]] to it[keys[1]]) as T
                }
            }

            Triple::class.java -> {
                return result.map {
                    val keys = it.keys.toList()
                    Triple(it[keys[0]], it[keys[1]], it[keys[2]]) as T
                }
            }
        }


        val companion = clazz.kotlin.companionObjectInstance ?: throw Exception("实体类需要添加伴生对象")
        val companionClass = companion::class
        val columns = companionClass.declaredMemberProperties
            .map { it.getter.call(companion) to it.name }
            .filter { it.first is QueryTableColumn }
            .map { (it.first as QueryTableColumn).column to it.second }
            .toMap()

        return result.map {
            val row = clazz.newInstance()

            columns.forEach { column ->
                val fieldName = column.value
                val field = (clazz.kotlin.declaredMembers.find { it.name == fieldName } as KProperty).javaField
                field?.isAccessible = true
                field?.set(row, it[column.key])
            }

            row
        }
    }

    /**
     * 将数据绑定到实体类中
     * @param result List<Map<String, Any?>> 数据库查询结果集
     * @return List<T> 绑定结果集
     */
    inline fun <reified T> bind(result: List<Map<String, Any?>>): List<T> {
        when (T::class) {
            Int::class, Long::class, Float::class, Double::class, String::class, Date::class, BigDecimal::class -> {
                return result.map {
                    val keys = it.keys.toList()
                    val key = keys[0]
                    it[key] as T
                }
            }

            Pair::class -> {
                return result.map {
                    val keys = it.keys.toList()
                    (it[keys[0]] to it[keys[1]]) as T
                }
            }

            Triple::class -> {
                return result.map {
                    val keys = it.keys.toList()
                    Triple(it[keys[0]], it[keys[1]], it[keys[2]]) as T
                }
            }
        }

        val companion = T::class.companionObjectInstance ?: throw Exception("实体类需要添加伴生对象")
        val companionClass = companion::class
        val columns = companionClass.declaredMemberProperties
            .map { it.getter.call(companion) to it.name }
            .filter { it.first is QueryTableColumn }
            .map { (it.first as QueryTableColumn).column to it.second }
            .toMap()

        return result.map {
            val rowClass = T::class
            val row = rowClass.java.newInstance()

            columns.forEach { column ->
                val fieldName = column.value
                val field = (rowClass.declaredMembers.find { it.name == fieldName } as KProperty).javaField
                field?.isAccessible = true
                field?.set(row, it[column.key])
            }

            row
        }
    }

    /**
     * 将数据绑定到匿名对象中
     * @param obj Any 匿名对象
     * @param result List<Map<String, Any?>> 数据库查询结果集
     * @return List<T> 绑定结果集
     */
    fun <T : Any> bindAnonymousObjects(obj: T, result: List<Map<String, Any?>>): List<T> {
        val clazz = obj::class
        val properties = clazz.declaredMemberProperties

        return result.map {
            val constructor = clazz.java.getDeclaredConstructor()
            constructor.isAccessible = true
            val row = constructor.newInstance()
            properties.forEach { property ->
                if (it.containsKey(humpToLine(property.name))) {
                    val field = property.javaField
                    field?.isAccessible = true
                    field?.set(row, it[humpToLine(property.name)])
                }
            }
            row
        }
    }

    /**
     * 将查询结果集作为Map列表返回
     * @return List<Map<String, Any?>> 返回结果
     */
    fun queryMap(): List<Map<String, Any?>> {
        return query(sql(), isTransaction, conn!!, dbConnection)
    }

    /**
     * 将查询结果集作为实体列表返回
     * @param clazz Class<T> 实体类的java class
     * @return List<T> 实体列表
     */
    fun <T : Any> query(clazz: Class<T>): List<T> {
        val list = query(this.sql(), isTransaction, conn!!, dbConnection)

        return bind(clazz, list)
    }

    /**
     * 将查询结果集作为实体列表返回
     * @return List<T> 实体列表
     */
    inline fun <reified T> query(): List<T> {
        val list = query(this.sql(), isTransaction, conn!!, dbConnection)

        return bind(list)
    }

    /**
     * 将查询结果集作为匿名对象列表返回
     * @param obj T 匿名对象
     * @return List<T> 对象列表
     */
    fun <T : Any> queryAnonymousObjects(obj: T): List<T> {
        val list = query(this.sql(), isTransaction, conn!!, dbConnection)

        return bindAnonymousObjects(obj, list)
    }

    /**
     * 获取条数
     * @return Long 条数
     */
    open fun fetchCount(): Long {
        return queryCount(sql(), isTransaction, conn!!, dbConnection)
    }

    /**
     * 结果是否存在
     * @return Boolean
     */
    open fun exist(): Boolean {
        return fetchCount() > 0
    }

    /**
     * @return String
     */
    override fun toString(): String {
        return sql()
    }

    /**
     * 将查询dsl转换为表达式类型，以作为子查询带入其他外层查询dsl
     * @return QuerySub 查询dsl用的子查询表达式
     */
    override fun toExpr(): QuerySub {
        return QuerySub(this)
    }
}